package project2;


import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.ws.spi.http.HttpContext;

public class RequestThread extends Thread {
	private ProxyServerHttpContext context;
	private SocketThreadWrapper wrap;
	private int requestNum;
	
	public RequestThread(List<String> request, SocketThreadWrapper wrap, int requestNum) {
		super();
		this.wrap = wrap;
		this.requestNum = requestNum;
		final Map<String, String> kvPair = new HashMap<String, String>();
		
		if (request.size() < 2 || (!request.get(0).contains("GET ") && !request.get(0).contains("POST "))) {
			throw new HttpRequestFormatErrorException("Error Http Request Format " + request + ".");
		}
		final String p = request.get(0).split(" ")[1];
		final boolean isPost = request.get(0).contains("POST");
		for (int i = 1; i < request.size(); i++) {
			String[] raw = request.get(i).split(": ", 2);
			if (raw.length < 2) {
				throw new HttpRequestFormatErrorException("Error Http Request Format for request " + request + ".");
			}
			// Handle the keep-alive as requested.
			if (raw[0].equals("Connection") && raw[1].equals("keep-alive")) {
				raw[1] = "close";
			}
			kvPair.put(raw[0], raw[1]);
		}
		
		this.context = new ProxyServerHttpContext(kvPair, p, !isPost, request.get(0));
		
		Util.pm("Recieve new Request[" + requestNum + "]: " + request.get(0));
	}
	
	@Override
	public void run() {
		String[] hostport = context.getAttribute("Host").split(":");
		int port = 80;
		if (hostport.length > 1) {
			port = Integer.parseInt(hostport[1]);
		}
		
		try {
			final Socket serverConnectionSocket = new Socket(InetAddress.getByName(hostport[0]), port);
			final PrintWriter serverOut = new PrintWriter(serverConnectionSocket.getOutputStream(), true);
			final DataInputStream serverIn = new DataInputStream(serverConnectionSocket.getInputStream());
			final RequestThread thread = this;
			
			// Response thread.
			new Thread() {
				
				@Override
				public void run() {	
					System.out.println("[RequestThread] Trying to send back to ProxyServerWrapper. ");
					wrap.success(serverIn);
					
					// Remove this thread from the active thread list.
					wrap.removeFromActiveList(thread);
					
					try {
						serverIn.close();
						serverOut.close();
						serverConnectionSocket.close();
					} catch (IOException e) {
						wrap.failure(e, "Close the server-connected socket error.", true, requestNum);
					}
					Util.pl("Request[" + requestNum + "]: Finished. ");
				}
				
			}.start();
			
			// Send TCP request to http server.
			serverOut.println(context.getHead());
			for (String key : context.getAttributeNames()) {
				serverOut.println(key + ": " + context.getAttribute(key));
			}
			serverOut.println();
			System.out.println("[RequestThread] Finish sending to sever. ");
			
		} catch (UnknownHostException e) {
			wrap.failure(e, "The given host is unknown.", true, requestNum);
			return;
		} catch (IOException e) {
			wrap.failure(e, "IOException happens when connecting to the server.", true, requestNum);
			return;
		}
	}
	
}

class ProxyServerHttpContext extends HttpContext {
	private Map<String, String> kv;
	private String path;
	private boolean isGet;
	private String head;
	
	public ProxyServerHttpContext(Map<String, String> kvPair, String path, boolean isGet, String head) {
		this.kv = kvPair;
		this.path = path;
		this.isGet = isGet;
		this.head = head;
	}
	
	public String getHead() {
		return head;
	}
	
	@Override
	public String getPath() {
		return path;
	}
	
	@Override
	public Set<String> getAttributeNames() {
		return kv.keySet();
	}
	
	@Override
	public String getAttribute(String name) {
		return kv.get(name);
	}
	
	public boolean isGet() {
		return isGet;
	}
	
}
